/*
 * matrix.c
 *
 *  Created on: Nov 20, 2013
 *      Author: cinus
 */

#include "matrix.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

matrix_tp newMatrix(uint32 rows, uint32 cols) {
	matrix_tp m;
	uint32 matrixSize;

	m = (matrix_tp) malloc(sizeof(matrix_t));
	m->cols = cols;
	m->rows = rows;

	matrixSize = rows * cols;
	m->data = (float32*) calloc(matrixSize, sizeof(float32));

	return m;
}

matrix_tp copyMatrix(matrix_tp src) {
	matrix_tp mcopy;
	mcopy = newMatrix(src->rows, src->cols);
	memcpy(mcopy->data, src->data, src->rows * src->cols * sizeof(float32));
	return mcopy;
}

void zeros(matrix_tp m) {
	memset(m->data, 0, m->rows * m->cols * sizeof(float32));
}
/*
 * i -> row
 * j -> column
 *
 * |a	b |
 * |c   d |  ---> | a c e b d f |
 * |e   f |
 *
 * a[1,2] = b
 * a[3,1] = e
 *
 * scelto questo formato perché è più facile da ottenere
 * a partire dalla rappresentazione attuale della rete
 */
float32 get(matrix_tp m, uint32 i, uint32 j) {

	return m->data[j * m->rows + i]; //
}

void set(matrix_tp m, uint32 i, uint32 j, float32 val) {
	m->data[j * m->rows + i] = val;
}

void setColumn(matrix_tp m, uint32 column, float32* vector, int size) {
	if (size == 0) {
		memcpy(m->data + (column * m->rows), vector, m->rows * sizeof(float32));
	} else {
		memcpy(m->data + (column * m->rows), vector, size * sizeof(float32));
	}
}

void printMatrix(matrix_tp m) {
	uint32 i, j;
	fprintf(stderr, "<\n");
	for (i = 0; i < m->rows; i++) {
		fprintf(stderr, "| ");
		for (j = 0; j < m->cols; j++) {
			fprintf(stderr, "%f ", get(m, i, j));
		}
		fprintf(stderr, "|\n");
	}
	fprintf(stderr, ">\n");
}

void freeData(matrix_tp m) {
	m->rows = 0;
	m->cols = 0;
	free(m->data);
}

matrix_tp t(matrix_tp m) {
	matrix_tp tm;
	uint32 i, j;
	tm = newMatrix(m->cols, m->rows);
	for (i = 0; i < m->rows; i++) {
		for (j = 0; j < m->cols; j++) {
			set(tm, j, i, get(m, i, j));
		}
	}
	return tm;
}

matrix_tp multiply(matrix_tp a, matrix_tp b) {
	uint32 i, j, k;

	matrix_tp c;
	float32 tmp = .0f;
	c = newMatrix(a->rows, b->cols);
	for (i = 0; i < a->rows; i++) {
		for (j = 0; j < b->cols; j++) {
			for (k = 0; k < b->rows; k++) {
				tmp += get(a, i, k) * get(b, k, j);
			}
			set(c, i, j, tmp);
			tmp = .0f;
		}
	}
	return c;
}

matrix_tp hadamard(matrix_tp a, matrix_tp b) {
	uint32 i, j;
	if (a->rows != b->rows || a->cols != b->cols) {
		fprintf(stderr, "Matrix hadamard error, Wrong Dimensions\n");
		printMatrix(a);
		printMatrix(b);
		return NULL;
	}
	matrix_tp c;
	c = newMatrix(a->rows, a->cols);

	for (i = 0; i < c->rows; i++) {
		for (j = 0; j < c->cols; j++) {
			set(c, i, j, get(a, i, j) * get(b, i, j));
		}
	}
	return c;
}

matrix_tp add(matrix_tp a, matrix_tp b) {
	uint32 i, j;
	if (a->rows != b->rows || a->cols != b->cols) {
		fprintf(stderr, "Matrix addiction error, Wrong Dimensions\n");
		printMatrix(a);
		printMatrix(b);
		return NULL;
	}
	matrix_tp c;
	c = newMatrix(a->rows, a->cols);
	for (i = 0; i < a->rows; i++) {
		for (j = 0; j < a->cols; j++) {
			set(c, i, j, get(a, i, j) + get(b, i, j));
		}
	}
	return c;
}
matrix_tp sub(matrix_tp a, matrix_tp b) {
	matrix_tp tmp, res;

	tmp = mulScalar(b, -1.0f);
	res = add(a, tmp);
	freeData(tmp);
	free(tmp);
	return res;
}

matrix_tp mulScalar(matrix_tp a, float32 scalar) {
	uint32 i, j;

	matrix_tp c;
	c = newMatrix(a->rows, a->cols);
	for (i = 0; i < a->rows; i++) {
		for (j = 0; j < a->cols; j++) {
			set(c, i, j, get(a, i, j) * scalar);
		}
	}
	return c;
}
